﻿
#define SPADAS_DEPRECATED_HIDE
#include "NextSWScale.h"
#include "NextYUVConversion.h"
#include "ffmpegdl.h"

using namespace videonext;

SWScaleConverterBGRYUV::SWScaleConverterBGRYUV()
{
	bgr2yuv420p = 0;
}
Bool SWScaleConverterBGRYUV::open(Size2D frameSize)
{
	close();

	bgr2yuv420p = sws_getContext(frameSize.width, frameSize.height, AVPixelFormat::AV_PIX_FMT_BGR24, frameSize.width, frameSize.height, AVPixelFormat::AV_PIX_FMT_YUV420P, SWS_POINT, NULL, NULL, NULL);
	if (!bgr2yuv420p) return FALSE;

	size = frameSize;

	return TRUE;
}
void SWScaleConverterBGRYUV::close()
{
	if (bgr2yuv420p)
	{
		sws_freeContext(bgr2yuv420p);
		bgr2yuv420p = 0;
	}
}
Bool SWScaleConverterBGRYUV::convert(CompatibleImage src, CompatibleImage& dst)
{
	if (bgr2yuv420p == 0) return FALSE;
	if (src.width() != size.width || src.height() != size.height) return FALSE;
	if (src.getType() != CompatibleImageType::ImagePointerColor && src.getType() != CompatibleImageType::IplImageColor) return FALSE;

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != size.width || dst.height() != size.height)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, size.width);
		av_frame_set_height(frame, size.height);

		Int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	CompatibleChannelData channelData = src.channels()[0];
	Byte *bgrSource[3] = { channelData.data(), NULL, NULL };
	Int bgrStride[3] = { (int)channelData.step(), 0, 0 };

	Pointer dstFrame = dst.getImage();

	sws_scale(bgr2yuv420p, bgrSource, bgrStride, 0, size.height, av_frame_get_data(dstFrame), av_frame_get_linesize(dstFrame));

	return TRUE;
}

SWScaleConverterYUVBGR::SWScaleConverterYUVBGR()
{
	yuv420p2bgr = 0;
}
Bool SWScaleConverterYUVBGR::open(Size2D frameSize)
{
	close();

	yuv420p2bgr = sws_getContext(frameSize.width, frameSize.height, AVPixelFormat::AV_PIX_FMT_YUV420P, frameSize.width, frameSize.height, AVPixelFormat::AV_PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
	if (!yuv420p2bgr) return FALSE;

	size = frameSize;

	return TRUE;
}
void SWScaleConverterYUVBGR::close()
{
	if (yuv420p2bgr)
	{
		sws_freeContext(yuv420p2bgr);
		yuv420p2bgr = 0;
	}
}
Bool SWScaleConverterYUVBGR::convert(CompatibleImage src, CompatibleImage& dst)
{
	if (yuv420p2bgr == 0) return FALSE;
	if (src.width() != size.width || src.height() != size.height) return FALSE;
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	if (dst.getType() != CompatibleImageType::ImagePointerColor || dst.width() != size.width || dst.height() != size.height)
	{
		dst = CompatibleImage(ImagePointer(size, TRUE));
	}

	Pointer srcFrame = src.getImage();

	CompatibleChannelData channelData = dst.channels()[0];
	Byte *bgrSource[3] = { channelData.data(), NULL, NULL };
	Int bgrStride[3] = { (int)channelData.step(), 0, 0 };

	sws_scale(yuv420p2bgr, av_frame_get_data(srcFrame), av_frame_get_linesize(srcFrame), 0, size.height, bgrSource, bgrStride);

	return TRUE;
}

SWScaleResizerYUV::SWScaleResizerYUV()
{
	yuvResizer = 0;
}
Bool SWScaleResizerYUV::open(Size2D srcSize, Size2D dstSize)
{
	close();

	int mode = dstSize.width < srcSize.width || dstSize.height < srcSize.height ? SWS_BICUBIC : SWS_BILINEAR;
	yuvResizer = sws_getContext(srcSize.width, srcSize.height, AVPixelFormat::AV_PIX_FMT_YUV420P, dstSize.width, dstSize.height, AVPixelFormat::AV_PIX_FMT_YUV420P, mode, NULL, NULL, NULL);
	if (!yuvResizer) return FALSE;

	this->srcSize = srcSize;
	this->dstSize = dstSize;

	return TRUE;
}
void SWScaleResizerYUV::close()
{
	if (yuvResizer)
	{
		sws_freeContext(yuvResizer);
		yuvResizer = 0;
	}
}
Bool SWScaleResizerYUV::resize(CompatibleImage src, CompatibleImage& dst, Bool canRefer)
{
	if (yuvResizer == 0) return FALSE;
	if (src.width() != srcSize.width || src.height() != srcSize.height) return FALSE;
	if (src.getType() != CompatibleImageType::AVFrameYUV420P) return FALSE;

	if (canRefer && dstSize == srcSize)
	{
		dst = src;
		return TRUE;
	}

	if (dst.getType() != CompatibleImageType::AVFrameYUV420P || dst.width() != dstSize.width || dst.height() != dstSize.height)
	{
		Pointer frame = av_frame_alloc();
		if (!frame) return FALSE;

		av_frame_set_format(frame, AVPixelFormat::AV_PIX_FMT_YUV420P);
		av_frame_set_width(frame, dstSize.width);
		av_frame_set_height(frame, dstSize.height);

		int resultCreateFrameData = av_frame_get_buffer(frame, 0);
		if (resultCreateFrameData < 0) return FALSE;

		dst = CompatibleImage(frame, TRUE);
	}

	if (dstSize == srcSize)
	{
		Array<CompatibleChannelData> srcChannels = src.channels();
		Array<CompatibleChannelData> dstChannels = dst.channels();
		for (UInt i = 0; i < srcChannels.size(); i++)
		{
			if (i >= dstChannels.size()) break;
			Byte *srcData = srcChannels[i].data();
			Byte *dstData = dstChannels[i].data();
			UInt srcStep = srcChannels[i].step();
			UInt dstStep = dstChannels[i].step();
			UInt copySize = math::min(srcStep, dstStep);
			for (UInt v = 0; v < srcChannels[i].height(); v++)
			{
				if (v >= dstChannels[i].height()) break;
				utility::memoryCopy(&srcData[v * srcStep], &dstData[v * dstStep], copySize);
			}
		}
		return TRUE;
	}
	else if (src.width() == dst.width() * 2 && src.height() == dst.height() * 2)
	{
		return resizeYuv420Half(src, dst);
	}
	else if (src.width() == dst.width() * 3 && src.height() == dst.height() * 3)
	{
		return resizeYuv420OneThird(src, dst);
	}
	else if (src.width() == dst.width() * 4 && src.height() == dst.height() * 4)
	{
		return resizeYuv420Quarter(src, dst);
	}
	else if (src.width() == dst.width() * 6 && src.height() == dst.height() * 6)
	{
		return resizeYuv420OneSixth(src, dst);
	}
	else if (src.width() == dst.width() * 8 && src.height() == dst.height() * 8)
	{
		return resizeYuv420OneEighth(src, dst);
	}
	else
	{
		Pointer srcFrame = src.getImage();
		Pointer dstFrame = dst.getImage();

		sws_scale(yuvResizer, av_frame_get_data(srcFrame), av_frame_get_linesize(srcFrame), 0, srcSize.height, av_frame_get_data(dstFrame), av_frame_get_linesize(dstFrame));

		return TRUE;
	}
}